#include "course.h"
#include "context.h"
#include <algorithm>
#include "logger.h"
#include "norequirement.h"

using namespace std;

Course::Course() :
    prereqs(new NoRequirement)
{
	// TODO:  implement
	Logger::instance()->warning("Default constructor of Course was called but "
								"is not implemented yet.", 1);
}

Course::Course(const Course& other_course) :
	courseID(other_course.getCourseID()),
	description(other_course.getDescription()),
    credits(other_course.credits),
	prereqs(other_course.prerequisites())
	//,deps(other_course.dependents())
{
}

Course::Course(const std::string& new_courseID) :
	courseID(new_courseID),
    prereqs(new NoRequirement)
{
}

Course& Course::operator=(const Course& other_course)
{
	if( this == &other_course )
		return *this;
	courseID = other_course.getCourseID();
	description = other_course.getDescription();
    credits = other_course.credits;
	prereqs = other_course.prerequisites();
	//deps = other_course.dependents();
	return *this;
}

Course::~Course() // destructor
{

}

string Course::getCourseID() const
{
	return courseID;
}

string Course::getDescription() const
{
	return description;
}

double Course::getCredits() const
{
    return credits;
}

boost::shared_ptr<Requirement> Course::prerequisites() const
{
	return prereqs;
}

// vector<Course> Course::dependents() const
// {
// 	return deps;
// }

bool Course::operator==(const Course& other_course) const
{
	return getCourseID() == other_course.getCourseID();
}

bool Course::operator!=(const Course& other_course) const
{
	return ! (*this == other_course);
}

bool Course::operator<(const Course& other_course) const
{
	return getCourseID() < other_course.getCourseID();
}

bool Course::operator<=(const Course& other_course) const
{
	return *this < other_course || *this == other_course;
}

bool Course::operator>(const Course& other_course) const
{
	return ! (*this <= other_course);
}

bool Course::operator>=(const Course& other_course) const
{
	return *this > other_course || *this == other_course;
}

void Course::setDescription (const std::string& newDesc)
{
    description = newDesc;
    return;
}

// void Course::addDep(const Course& dep)
// {
// 	deps.push_back(dep);
// 	std::sort(deps.begin(), deps.end());
// }

ostream& operator<<(ostream& output, const Course& c)
{
    return output << c.getCourseID() << ". " << c.getDescription();
}
			
// void Course::removeDep(const Course& dep)
// {
// 	deps.erase(std::find(deps.begin(), deps.end(), dep));
// }

void Course::prerequisites(boost::shared_ptr<Requirement> requirements)
{
	prereqs = requirements;
}

void Course::setCredits(const double value)
{
	credits = value;
}

/// Evaluates whether the requirement of minimum number of credits is fulfilled
/// within the given context.
/// \param context The context within which the requirement is tested.
/// \return True if the number of credits the student in the given context has
/// accumulated is equal to or greater than this requirement's minimum credits
/// value.  False otherwise.
bool Course::evaluate(Context& context)
{
    boost::shared_ptr<Requirement> copy_of_this =
        boost::shared_ptr<Requirement>(new Course(*this));
    return context.requirement_met(copy_of_this);
}

/// Generates a string representation of this course requirement.
/// \param context Merely fulfills the interface signature of the function.
/// Not used.
/// \return The string representation of this course requirement.
std::string Course::to_string(Context&)
{
    return getCourseID();
}

/// Returns 1 as the count of requirements this course requirement presents.
/// \param context Merely fulfills the interface signature of the function.
/// Not used.
/// \return The number of requirements this course requirement presents, which
/// is always 1.
std::size_t Course::count(Context&)
{
    return 1;
}

std::vector< boost::shared_ptr<CourseSection> > Course::getLectures() const
{
	return sections_;
}
/// Adds a section to this course.
/// \param section The section to be added.
/// \return Returns success status, not currently used.
bool Course::addSection(boost::shared_ptr<CourseSection> section)
{
	sections_.push_back(section);
	return true;
}

/// Gets this Course object in a list, to complete the courses extraction in
/// the requirements tree.
/// \return This Course object in a list.
std::list<Course> Course::courses()
{
    std::list<Course> this_course_in_list;
    this_course_in_list.push_back(*this);
    return this_course_in_list;
}
